package per.dj.testpro.entity.protocol;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import lombok.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import per.dj.testpro.utils.common.Tools;

import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

@Data
public class Message {

    private final static Logger LOGGER = LoggerFactory.getLogger(Message.class);

    private MessageHeader header = new MessageHeader();

    public static ConcurrentMap<String, List<byte[]>> msgMap = new ConcurrentHashMap<String, List<byte[]>>();

    private byte[] buffer = new byte[1024];

    private static byte preID = 0x7E;

    private IMessageBody messageContents;

    private byte[] checkSum = null;

    private static byte afterId = 0x7E;

    public final byte[] WriteToBytes() {

        byte[] bodyBytes = null;
        if (getMessageContents() != null) {
            bodyBytes = getMessageContents().WriteToBytes();
        }

        byte[] headerBytes = header.WriteToBytes();

        int bodyLength = 0;
        if(bodyBytes!=null){
            bodyLength = bodyBytes.length;
        }
        ByteBuf buff = Unpooled.buffer(headerBytes.length + bodyLength + 1);
        try {
            buff.writeBytes(headerBytes);
            if(bodyBytes != null){
                buff.writeBytes(bodyBytes);
            }
            byte[] messageBytes = buff.array();
            byte checkSum = GetCheckXor(messageBytes, 0, messageBytes.length);
            buff.writeByte(checkSum);
            byte[] escapedBytes = Escape(buff.array()); // 转义
            ByteBuf escapedBuff = Unpooled.buffer(escapedBytes.length + 2);
            try{
                escapedBuff.writeByte(preID);
                escapedBuff.writeBytes(escapedBytes);
                escapedBuff.writeByte(afterId);
                byte[] data = escapedBuff.array();
                LOGGER.info("下发数据包:"+Tools.ToHexString(data));
                return data;
            }finally {
                if(escapedBuff != null){
                    escapedBuff.release();
                }
            }

        }finally {
            if(buff != null){
                buff.release();
            }
        }

    }

    public final void ReadFromBytes(byte[] messageBytes) {

        byte[] validMessageBytes = UnEscape(messageBytes);
        // setPacketDescr(ParseUtil.ToHexString(messageBytes, 0,
        // messageBytes.length));
        try {
            // 检测校验码
			byte xor = GetCheckXor(validMessageBytes, 1,
					validMessageBytes.length - 2);
			byte realXor = validMessageBytes[validMessageBytes.length - 2];
			if (xor == realXor) {
				checkSum = new byte[] { xor };
            try {
                try {
                    int tempLen = validMessageBytes.length - 1 - 1 - 1;
                    byte[] headerBytes = new byte[12];
                    System.arraycopy(validMessageBytes, 1, headerBytes, 0,
                            headerBytes.length);
                    header.ReadFromBytes(headerBytes);

                    int start = 13;

                    if (header.getMessageSize() > 0) {
                        byte[] sourceData = new byte[header
                                .getMessageSize() - 12];
                        System.arraycopy(validMessageBytes, start,
                                sourceData, 0, sourceData.length);

                        setMessageContents(MessageFactory.Create(
                                header.getMessageId(), sourceData));

                    }
                } finally {
                    // binaryReader.dispose();
                }
            } finally {
                // ms.dispose();
            }
			} else {
				LOGGER.error("校验码不正确:"
						+ Tools.ToHexFormatString(messageBytes));
			}

        } catch (Exception ex) {
            LOGGER.error("Message : ReadFromBytes() "
                    + Tools.ToHexFormatString(messageBytes));
            LOGGER.error("解析异常", ex);
        }

    }

    /**
     * 获取校验和
     *
     * @param data
     * @param pos
     * @param len
     * @return
     */
    private byte GetCheckXor(byte[] data, int pos, int len) {
        byte A = 0;
        for (int i = pos; i < len; i++) {
            A ^= data[i];
        }
        return A;
    }

    /**
     * 将标识字符的转义字符还原
     *
     * @param data
     * @return
     */
    private byte[] UnEscape(byte[] data) {

        ByteBuf buff = Unpooled.buffer(data.length);
        try{
            for (int i = 0; i < data.length; i++) {
                if (data[i] == 0x7D) {
                    if (data[i + 1] == 0x01) {
                        buff.writeByte((byte) 0x7D);
                        i++;
                    } else if (data[i + 1] == 0x02) {
                        buff.writeByte((byte) 0x7E);
                        i++;
                    }
                } else {
                    buff.writeByte(data[i]);
                }
            }
            byte[] a = buff.array();
            return a;
        } catch (Exception e) {
            LOGGER.error(e.getMessage());
            return null;
        } finally {
            if(buff!=null)
            {
                buff.release();
            }
        }
    }

    /**
     * 加入标示符的转义进行封装
     *
     * @param data
     * @return
     */
    private byte[] Escape(byte[] data) {

        int length = 0;
        for (int j = 0; j < data.length; j++) {
            if (data[j] == 0x7D) {
                length++;
            } else if (data[j] == 0x7E) {
                length++;
            }
        }

        ByteBuf tmp = Unpooled.buffer(data.length+length);
        try{
            for (int j = 0; j < data.length; j++) {
                if (data[j] == 0x7D) {
                    tmp.writeByte((byte) 0x7D);
                    tmp.writeByte((byte) 0x01);
                } else if (data[j] == 0x7E) {
                    tmp.writeByte((byte) 0x7D);
                    tmp.writeByte((byte) 0x02);
                } else {
                    tmp.writeByte(data[j]);
                }
            }
            byte[] a = tmp.array();
            return a;
        }catch (Exception e){
            LOGGER.error(e.getMessage());
            return null;
        }finally {
            if(tmp!=null)
            {
                tmp.release();
            }
        }

    }
}
